{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sudo Test Scenarios\n",
    "\n",
    "Obviously, automated tests that require going through a UAC are challenging to write as unit tests, or run in CI. This notebook instead provides a way of listing a bunch of manual tests. This is all powered by the VsCode Polyglot Notebooks extension. Make sure you have that installed, so that you can run this notebook. \n",
    "\n",
    "(You'll need the [Polyglot Notebooks](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) extension for VsCode installed to run this notebook.)\n",
    "\n",
    "## Building\n",
    "\n",
    "First, start by building the code. We're going to stick the output exe into a `_sudo_` alias, to keep tests concise. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "cargo build --target x86_64-pc-windows-msvc\n",
    "new-alias -Force _sudo_ ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "new-alias -Force _sudo_ ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tests\n",
    "\n",
    "### Simple sanity tests\n",
    "Just start py printing the error message:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dismiss this UAC. We should print an error message."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ cmd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo notepad` should elevate Notepad directly without triggering a UAC prompt for `notepad`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ notepad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo --newWindow cmd` should elevate \"Command Prompt\" directly and spawn a new conhost."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ --newWindow cmd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo netstat -ab` should display network statistics and the process using them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ netstat -ab"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Exiting `netstat` and `sudo` using Ctrl+C should terminate the processes. This test is going to spawn a new CMD in a new conhost window, ctrl+c in that window to verify it did work. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "conhost -- cmd.exe /k ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe netstat -ab"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Redirecting to pipes and files\n",
    "\n",
    "This command uses `dir /b` to list the contents of the directory, and `find /c /v \"\"` to count the lines (items) returned.\n",
    "This should print a number greater than 0 if there are items in the directory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ cmd /c dir /b \"C:\\Program Files\\WindowsApps\" `| find /c /v \"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Env vars\n",
    "\n",
    "Running `sudo cmd`, without -E, doesn't propogate the environment variables. You should get `FooBar was %FooBar%`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "$env:FooBar = \"Hello World\"\n",
    "_sudo_ cmd /c echo FooBar was '%FooBar%'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo -E ...` should pass the env vars to the child process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "$env:FooBar = \"Hello World\"\n",
    "_sudo_ -E cmd /c echo FooBar was '%FooBar%'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo -E` with no command should not crash."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ -E"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo -N -E cmd` should elevate `sudo` directly, spawn a new conhost, and retain env vars. (The output will appear here in the notebook - close the conhost to continue.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "$env:FooBar = \"Hello new window\"\n",
    "_sudo_ --new-window -E cmd /k echo FooBar was '%FooBar%'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe cmd` from an admin prompt should silently launch CMD without triggering UAC. (you'll need to paste this into the conhost that appears manually)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "Start-Process -verb runas cmd -ArgumentList \"/k cd /d $((Get-Location).Path)\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### CMD intrinsics tests\n",
    "\n",
    "Running `sudo dir`, FROM CMD, should list the files in the current working directory. (Recall, we're in the `/tools` dir)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "cmd /c ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe dir"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, the notebook is running PowerShell. So, running `sudo dir` from PowerShell should not list files in the current working directory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ dir"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running the command `target\\x86_64-pc-windows-msvc\\debug\\sudo.exe fsutil volume allocationReport C: > out.txt`, then typing `out.txt` should display content in the text file.\n",
    "\n",
    "Ensure that the text file is not empty. This usually takes like 15-20 seconds to run, so patience. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "if (Test-Path -Path \"out.txt\") { Remove-Item -Path \"out.txt\" }\n",
    "_sudo_ fsutil volume allocationReport C: > out.txt\n",
    "(get-childItem -path out.txt).Length -gt 0\n",
    "Remove-Item -Path \"out.txt\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setting Modes\n",
    "\n",
    "\n",
    "Running `sudo --inline cmd` should exit with an error when `sudo` is set to disable input mode."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe config --enable disableInput\n",
    "_sudo_ --inline cmd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Same as above, but with `sudo --disable-input cmd` should exit with an error when `sudo` is set to force new window mode."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe config --enable forceNewWindow\n",
    "_sudo_ --disable-input cmd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, back to normal mode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe config --enable normal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Working directories\n",
    "\n",
    "Running `sudo -N cmd` in any path should start `cmd` in `C:\\windows\\system32`. (this will open a conhost in a new window)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ -N cmd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running `sudo -D . -N cmd` in any path should start in the original path. (the `/tools` directory)\n",
    "\n",
    "(again, output will actually appear in the notebook, close the conhost to continue)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ -D . -N cmd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This next one is a bit wacky. Search paths are... complicated. \n",
    "\n",
    "We're going to copy our built binary into system32 (under a different name, so it doesn't kill your existing sudo). \n",
    "Then, we're going to copy `cmd` into a different path, one that's relative to our CWD. \n",
    "\n",
    "Then, we're going to run `sudo2 ..\\cmd.exe`. This should run the `cmd` from the relative path, not the one in system32. We can verify this by looking at the conhost that's spawned. It should display an error at the start like:\n",
    "\n",
    "```\n",
    "The system cannot find message text for message number 0x2350 in the message file for Application.\n",
    "\n",
    "(c) Microsoft Corporation. All rights reserved.\n",
    "Not enough memory resources are available to process this command.\n",
    "```\n",
    "\n",
    "There will be two UACs to accept. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "pwsh"
    },
    "polyglot_notebook": {
     "kernelName": "pwsh"
    }
   },
   "outputs": [],
   "source": [
    "_sudo_ cmd /c copy ..\\target\\x86_64-pc-windows-msvc\\debug\\sudo.exe C:\\Windows\\System32\\sudo2.exe /y\n",
    "copy-item c:\\windows\\system32\\cmd.exe -destination .. -force\n",
    "c:\\windows\\system32\\sudo2.exe -N ..\\cmd.exe\n",
    "rm ..\\cmd.exe"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (PowerShell)",
   "language": "PowerShell",
   "name": ".net-pwsh"
  },
  "language_info": {
   "name": "polyglot-notebook"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "pwsh",
    "items": [
     {
      "aliases": [],
      "languageName": "pwsh",
      "name": "pwsh"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
